package org.game.snake.support;

import javafx.application.Platform;
import javafx.event.EventType;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import org.game.cell.CellFactory;
import org.game.cell.config.Cell;
import org.game.cell.support.DefaultCellFactory;
import org.game.constant.APIConstant;
import org.game.support.TopStatusBarController;
import org.game.util.MapUtil;
import org.game.util.RandomUtil;
import org.game.util.StringUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public abstract class SnakeMap extends TopStatusBarController {
    @FXML
    protected ImageView reset;

    protected GridPane snakeMap;

    protected GridPane lastMap;

    protected CellFactory cellFactory = new DefaultCellFactory();

    protected volatile KeyCode direction;

    protected int[][] directions = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};

    /**
     * 记录地图信息
     */
    protected Cell[][] cells;

    /**
     * 记录蛇的身体
     */
    protected List<Integer[]> snake;

    protected void initSnakeMap() {
        lastMap = snakeMap;
        snakeMap = new GridPane();
        snakeMap.setAlignment(Pos.CENTER);
        snakeMap.addEventHandler(KeyEvent.ANY, keyEvent -> {
            KeyCode keyCode = keyEvent.getCode();

            direction = keyCode;

            if (keyEvent.getEventType() != KeyEvent.KEY_PRESSED) {
                return;
            }

            moveSnake();
        });

        direction = null;

        int[][] map = new int[(int) APIConstant.CONFIG.get("width")][(int) APIConstant.CONFIG.get("height")];
        cells = new Cell[(int) APIConstant.CONFIG.get("width")][(int) APIConstant.CONFIG.get("height")];

        generateSnakeHeaderAndFoodPos(map);
        generateCell(map);
    }

    private void moveSnake() {
        if (direction == KeyCode.W) {
            // 向上运动
            move(directions[1]);
        } else if (direction == KeyCode.S) {
            // 向下运动
            move(directions[2]);
        } else if (direction == KeyCode.A) {
            // 向左运动
            move(directions[0]);
        } else if (direction == KeyCode.D) {
            // 向右运动
            move(directions[3]);
        }
    }

    protected void generateSnakeHeaderAndFoodPos(int[][] map) {
        int size = (int) APIConstant.CONFIG.get("width") * (int) APIConstant.CONFIG.get("height");
        int pos;
        int x;
        int y;

        while (true) {
            pos = RandomUtil.randomInt(size);
            x = pos % (int) APIConstant.CONFIG.get("width");
            y = pos / (int) APIConstant.CONFIG.get("height");

            if (map[x][y] == 0) {
                map[x][y] = 1;
                snake = new ArrayList<>();
                snake.add(new Integer[]{x, y});
                map[x - 1][y] = 3;
                snake.add(new Integer[]{x - 1, y});
                break;
            }
        }

        while (true) {
            pos = RandomUtil.randomInt(size);
            x = pos % (int) APIConstant.CONFIG.get("width");
            y = pos / (int) APIConstant.CONFIG.get("height");

            if (map[x][y] == 0) {
                map[x][y] = 2;
                return;
            }
        }
    }

    protected void generateFoodPos() {
        int size = (int) APIConstant.CONFIG.get("width") * (int) APIConstant.CONFIG.get("height");
        int pos;
        int x;
        int y;

        while (true) {
            pos = RandomUtil.randomInt(size);
            x = pos % (int) APIConstant.CONFIG.get("width");
            y = pos / (int) APIConstant.CONFIG.get("height");

            if (cells[x][y].getMode() == 0) {
                cells[x][y].setMode(2);
                cells[x][y].showFoodColor();
                return;
            }
        }
    }

    protected void generateCell(int[][] map) {
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                Cell cell = cellFactory.getCell(map[i][j], i, j);

                setCellColor(cell);

                cells[i][j] = cell;
                snakeMap.add(cell, i, j);
            }
        }
    }

    private void move(int[] directions) {
        if (MapUtil.isOutOfBounds(
                snake.get(0)[0] + directions[0],
                snake.get(0)[1] + directions[1],
                cells.length, cells[0].length)) {
            return;
        }

        if (cells[snake.get(0)[0] + directions[0]][snake.get(0)[1] + directions[1]].getMode() == 3) {
            death();
        }

        int size = snake.size();

        cells[snake.get(size - 1)[0]][snake.get(size - 1)[1]].showDefaultColor();
        cells[snake.get(size - 1)[0]][snake.get(size - 1)[1]].setMode(0);

        cells[snake.get(0)[0]][snake.get(0)[1]].showBodyColor();
        cells[snake.get(0)[0]][snake.get(0)[1]].setMode(3);

        for (int i = size - 1; i >= 1; i--) {
            snake.get(i)[0] = snake.get(i - 1)[0];
            snake.get(i)[1] = snake.get(i - 1)[1];
        }

        snake.get(0)[0] += directions[0];
        snake.get(0)[1] += directions[1];

        cells[snake.get(0)[0]][snake.get(0)[1]].showHeaderColor();

        if (cells[snake.get(0)[0]][snake.get(0)[1]].getMode() == 2) {
            addSnake(size);
            generateFoodPos();
        }

        cells[snake.get(0)[0]][snake.get(0)[1]].setMode(1);
    }

    private void addSnake(int size) {
        Integer[] lastPost = snake.get(size - 1);

        for (int[] pos : directions) {
            if (MapUtil.isOutOfBounds(
                    lastPost[0] + pos[0],
                    lastPost[1] + pos[1],
                    cells.length, cells[0].length) &&
                    cells[lastPost[0] + pos[0]][lastPost[1] + pos[1]].getMode() != 0) {
                continue;
            }

            snake.add(new Integer[]{lastPost[0] + pos[0], lastPost[1] + pos[1]});
            cells[lastPost[0] + pos[0]][lastPost[1] + pos[1]].showBodyColor();

            return;
        }
    }

    protected void setCellColor(Cell cell) {
        int mode = cell.getMode();

        if (mode == 1) {
            cell.showHeaderColor();
        } else if (mode == 2) {
            cell.showFoodColor();
        } else if (mode == 3) {
            cell.showBodyColor();
        }
    }

    private void death() {
        lockCells();

        reset.setImage(new Image("/image/cry.png"));
    }

    private void lockCells() {
        for (int i = 0; i < cells.length; i++) {
            for (int j = 0; j < cells[i].length; j++) {
                cells[i][j].setDisable(true);
                cells[i][j].showClickColor();
            }
        }
    }

    class Move extends Thread {
        @Override
        public void run() {
            while (isStop) {

            }

            if (direction == null) {
                return;
            }
            moveSnake();
        }
    }
}
